package com.zbj.homemade.spring.aop.invocation;

import com.zbj.homemade.spring.aop.interceptor.AroundInterceptor;
import com.zbj.homemade.spring.aop.interceptor.BeforeInterceptor;
import com.zbj.homemade.spring.aop.interceptor.MethodInterceptor;
import com.zbj.homemade.spring.utils.CollectionsUtil;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.List;

/**
 * |                    .  ’   *   ‘  .
 * |               _6                    "     o0000o
 * |  o00o _    .%                         %. o0000000o
 * | o000000o.:                              :(0000000o
 * | o00000os                                  .000000o
 * |  o0000/     <b>不了不了,改需求真的伤身体</>   .000o
 * |    o0o                                       .0o
 * |     y        <b>我认为源代码,就是SkrSkr</>     .
 * |    y                                           ,
 * |   y          <b>小熊猫保佑,代码永不修改</>        ,
 * |   p                            .                 .
 * |   p                           @@.                .
 * |    y                        ;@@@;               .
 * |    \                       ;@@@@;.@.          ,;S
 * |     、                     .@@@@@@;       ,@@@@@
 * |     @\.            .****...*@@@@;     .@@@@@@@@@@@.
 * |    @@@@\.     . @@@@@@@@@@@@@@@;    .@@@@@@@@@@@@@@@.
 * |   @@@@@@@@@.@@@@@@@@@@@@@@@@;   .@@@@@@@@@@@@@@@@@@@@@.
 * |  @@@@@@..@@@@..@@@@@@..@@@@@ .@@...@@@@@..@@..@@@@@..@@.
 * | @@@@@@@@..@..@@@@@@@@@@@@@;.@@@..@..@@@..@@....@@@..@@@@.
 * |@y@@@@@@@@..@@@@@@@@..@@@@i@@@@..@@@..@..@@..@@..@..@@@@n@.
 * |@n@@@@@@@..@@@@@@@@..@@@@@@@@@..@@@@@...@@..@@@@...@@@@@n@@.
 * |@@@@@@@@..@@@@@@@@..@@@@@@@@@..@@@@@@@.@@..@@@@@..@@@@@@@@@@.
 * |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@.
 *
 * @program: principle
 * @description:
 * @author: Yinn
 * @create: 2019-03-07 16:05
 **/
public class SimpleInvocation implements MethodInvocation {

    /**
     * 拦截链
     */
    private final List<MethodInterceptor> interceptors;

    /**
     * 被切入方法
     */
    private final Method currentMethod;

    /**
     * 代理对象
     */
    private final Object proxy;

    /**
     * 原始对象
     */
    private final Object ori;

    /**
     * 参数
     */
    private final Object[] args;

    /**
     * 当前执行到的拦截点
     */
    private int currentInterceptorIndex = -1;

    public SimpleInvocation(List<MethodInterceptor> interceptors, Method currentMethod, Object proxy, Object ori, Object[] args) {
        this.interceptors = interceptors;
        this.currentMethod = currentMethod;
        this.proxy = proxy;
        this.ori = ori;
        this.args = args;
    }

    /**
     * spring中拦截链是倒叙的 1 afterThrowing -->...--> 5 before
     *
     * 此处设计的十分巧妙，结合五种通知，倒叙递归的方式。
     * 目的：先让所有通知的invoke方法执行完，依靠倒退的方式，逐渐插入原始方法的执行时机
     *
     * 详解：
     *
     * 假如五种通知都存在
     *
     * 拦截链的顺序： 最上方是list的第0位，最末尾是 最先执行的通知方法.
     *   拦截链             具体实现  （被Aop注解的切面方法用anno()代替，原始方法用self()代替）
     * | AfterThrowing      proceed() -> anno()
     * | AfterReturning     proceed() -> anno()
     * | After              proceed() -> anno()
     * | Around             anno()  {@text: 特殊通知：通过调用者控制原方法的调用时机，假如@Around打在了一个空方法上，Before不会生效}
     * | Before             anno() -> proceed()
     * V
     *
     *                                                  假如使用者调用切点接口中的proceed()
     *               @AfterThrowing     @AfterReturning     @After       @Around    @Before     @Before                @Around      @After    @AfterReturning     @AfterThrowing
     * 调用逻辑 应该是  proceed()    ->     proceed()   ->   proceed() -> anno() ->  anno()  ->  proceed() -> self() -> anno()   ->  anno()  ->   anno()     ---->    anno()
     *
     * 读的时候多捋几次就ok了。
     */
    @Override
    public Object proceed() throws Throwable {

        //无任何拦截 ，直接执行被切方法
        if (CollectionsUtil.isEmpty(interceptors))
            return invokeCurrentMethod();

        /**
         *
         */
        if (currentInterceptorIndex == interceptors.size() - 1)
            return invokeCurrentMethod();

        /**
         * 执行
         */
        return interceptors.get(++currentInterceptorIndex).invoke(this);
    }

    /**
     * 执行被切方法
     */
    private Object invokeCurrentMethod() throws InvocationTargetException, IllegalAccessException {
        return currentMethod.invoke(ori, args);
    }

    @Override
    public Method getMethod() {
        return currentMethod;
    }

    @Override
    public Object getThis() {
        return ori;
    }

    @Override
    public Object[] getArgs() {
        return args;
    }

    @Override
    public Object getProxyTarget() {
        return proxy;
    }
}
